/*
-----------------------------------------------------------------------------
Filename:    TutorialApplication.cpp
-----------------------------------------------------------------------------

This source file is part of the
   ___                 __    __ _ _    _ 
  /___\__ _ _ __ ___  / / /\ \ (_) | _(_)
 //  // _` | '__/ _ \ \ \/  \/ / | |/ / |
/ \_// (_| | | |  __/  \  /\  /| |   <| |
\___/ \__, |_|  \___|   \/  \/ |_|_|\_\_|
      |___/                              
      Tutorial Framework
      http://www.ogre3d.org/tikiwiki/
-----------------------------------------------------------------------------
*/
#include "OgreApplication.h"
#include <QDebug>
#include <QtCore/QCoreApplication>
//-------------------------------------------------------------------------------------
OgreApplication::OgreApplication() : QObject(0)
{
    m_keepRunning = true;
    m_paused = false;
}
//-------------------------------------------------------------------------------------
OgreApplication::~OgreApplication(void)
{
}

//-------------------------------------------------------------------------------------
void OgreApplication::createScene(void)
{
    mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8, 500);
}

Ogre::Entity* OgreApplication::createEntity(OEntity* entity)
{
    qDebug() << "***********creating mesh";
    Ogre::SceneNode* node = mSceneMgr->getSceneNode(entity->nodeId().toStdString());
    Ogre::Entity* ent = mSceneMgr->createEntity(entity->id().toStdString(), entity->model().toStdString());
    node->attachObject(ent);

    if (ent != 0) {
        ent->setCastShadows(entity->castShadows());
        if (entity->materialName().length() > 0) {
            ent->setMaterialName(entity->materialName().toStdString());
        }
    }

    return ent;
}

Ogre::SceneNode* OgreApplication::createNode(ONode* node)
{
    qDebug() << "***********creating node";

    Ogre::SceneNode* headNode = 0;
    if (node->parentNodeId().compare("root") == 0) {
        headNode = mSceneMgr->getRootSceneNode();
    } else {
        headNode = mSceneMgr->getSceneNode(node->parentNodeId().toStdString());
    }

    if (headNode == 0) return 0;

    return headNode->createChildSceneNode(node->id().toStdString());
}

Ogre::Plane* OgreApplication::createPlane(OPlane *plane)
{
    qDebug() << "***********creating plane";

    Ogre::Plane* pl = new Ogre::Plane(plane->xNormalPos(), plane->yNormalPos(), plane->zNormalPos(), plane->dist());

    Ogre::MeshManager::getSingleton().createPlane(plane->model().toStdString(), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
                                                  *pl, plane->width(), plane->height(), 20, 20, true, 1, 5, 5, Ogre::Vector3(plane->xUpPos(), plane->yUpPos(), plane->zUpPos()));

    createEntity((OEntity*)plane);

    return pl;
}

Ogre::Camera* OgreApplication::createCamera(OCamera *camera){
    qDebug() << "***********creating camera "<< camera->id();
    return mSceneMgr->createCamera(camera->id().toStdString());
}

Ogre::Light* OgreApplication::createLight(OLight *light)
{
    qDebug() << "***********creating light";

    Ogre::Light* ogrLight = mSceneMgr->createLight(light->id().toStdString());
    switch (light->lightType()) {
    case DIR: {
        ogrLight->setType(Ogre::Light::LT_DIRECTIONAL);
    } break;
    case POINT: {
        ogrLight->setType(Ogre::Light::LT_POINT);
    } break;
    case SPOT: {
        ogrLight->setType(Ogre::Light::LT_SPOTLIGHT);
    } break;
    }
    ogrLight->setPosition(Ogre::Vector3(light->xPos(), light->yPos(), light->zPos()));
    ogrLight->setSpecularColour(Ogre::ColourValue(light->specRed(), light->specGreen(), light->specBlue()));
    ogrLight->setDiffuseColour(Ogre::ColourValue(light->difRed(), light->difGreen(), light->difBlue()));

    Ogre::SceneNode* node = 0;
    if (light->nodeId().length() > 0)
        node = mSceneMgr->getSceneNode(light->nodeId().toStdString());

    if (node != 0) {
        node->attachObject(ogrLight);
    }

    return ogrLight;
}

void OgreApplication::renderOneFrame()
{
    Ogre::WindowEventUtilities::messagePump();
    m_keepRunning = mRoot->renderOneFrame();
}

bool OgreApplication::keepRunning()
{
    return m_keepRunning;
}

bool OgreApplication::prepare()
{

#ifdef _DEBUG
    mResourcesCfg = "resources_d.cfg";
    mPluginsCfg = "plugins_d.cfg";
#else
    mResourcesCfg = "resources.cfg";
    mPluginsCfg = "plugins.cfg";
#endif

    if (!setup())
        return false;
    return true;
}

void OgreApplication::processObjQueue()
{
    // process nodes first, because the other objects might require to be attached to them
    while (!nodeQueue->empty()) {
        ONode* node = nodeQueue->first();
        nodeQueue->removeFirst();

        Ogre::SceneNode* chNode = createNode(node);
        if (chNode != 0) {
            node->setNode(chNode);
        }
    }

    while (!objectQueue->empty()) {

        BasicObject* obj = objectQueue->first();
        objectQueue->removeFirst();

        if (obj->type() == ENTITY) {
            OEntity* entity = (OEntity*)obj;
            createEntity(entity);
        } else if (obj->type() == PLANE) {
            OPlane* plane = (OPlane*)obj;
            Ogre::Plane* pl = createPlane(plane);
            if (pl != 0) {
                plane->setPlane(pl);
            }
        } else if (obj->type() == LIGHT) {
            OLight* light = (OLight*)obj;
            Ogre::Light* pl = createLight(light);
            if (pl != 0) {
                light->setLight(pl);
            }
        }  else if (obj->type() == CAMERA) {
            OCamera* camera = (OCamera*)obj;
            Ogre::Camera* native_camera = createCamera(camera);
            if (native_camera != 0) {
                camera->setNativeCamera(native_camera);
                camera->applyPos();
                camera->applyLookAt();
                qDebug() << "***********created camera:"
                         << native_camera->getPosition().x<<" "
                         << native_camera->getPosition().y<<" "
                         << native_camera->getPosition().z;
            }
        }
    }
}

void OgreApplication::processCmdQueue()
{
    while (!cmdQueue->empty()) {
        Command c = cmdQueue->first();
        cmdQueue->removeFirst();

        if (c == STOP) {
            m_keepRunning = false;
        } else if (c == PAUSE) {
            m_paused = true;
        } else if (c == RESUME) {
            m_paused = false;
        }
    }
}

void OgreApplication::run()
{
    prepare();

    processObjQueue();

    emit ready(mSceneMgr);

    while (keepRunning()) {

        time.restart();

        processCmdQueue();
        processObjQueue();

        if (!m_paused) {
            //            time.restart();

            renderOneFrame();

            int elTime = time.elapsed();

            emit frameRendered(elTime);
        }
    }

    destroyScene();

    emit stopped();
}

bool OgreApplication::keyPressed(const OIS::KeyEvent &arg)
{
    emit keyPress(arg.key);
    return BaseApplication::keyPressed(arg);
}

void OgreApplication::setMainCamera(Ogre::Camera* camera){
    mCamera = camera;
    mVp->setCamera(mCamera);
    mCamera->setAspectRatio(
                Ogre::Real(mVp->getActualWidth()) / Ogre::Real(mVp->getActualHeight()));
}
